using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using Ricciolo.PaperBoy.Feeds;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using System.Windows.Forms.Design;
using System.IO;
using System.Net;
using System.Configuration;
using Ricciolo.PaperBoy.UI.Converters;

namespace Ricciolo.PaperBoy.UI
{

    public partial class MainWindow : System.Windows.Window
    {

        private FeedsManager feedsManager;
        private System.Windows.Forms.NotifyIcon notifyIcon;
        private readonly ApplicationSettingsBase _applicationSettings;
        private Dictionary<String, FeedSetting> _feedSettings;

        private static readonly DependencyProperty CurrentFeedProperty = DependencyProperty.Register("CurrentFeed", typeof(IFeed), typeof(MainWindow), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCurrentFeedChanged)));
        private CollectionViewSource feedSource;

        public MainWindow()
        {
            PrepareCommandBindings();
            InitData();

            base.Title = String.Format("Ricciolo - PaperBoy {0}", typeof(MainWindow).Assembly.GetName().Version);
        }

        public MainWindow(ApplicationSettingsBase applicationSettings)
            : this()
        {
            this._applicationSettings = applicationSettings;
        }

        /// <summary>
        /// Create the settings property collections to store for personalization
        /// </summary>
        protected virtual void CreateSettingsProperties()
        {
            this.CreateSettingsProperty("Win_Width", 900d);
            this.CreateSettingsProperty("Win_Height", 700d);
            this.CreateSettingsProperty("Win_State", WindowState.Normal);
            this.CreateSettingsProperty("FeedSettings", new Dictionary<String, FeedSetting>());
        }

        /// <summary>
        /// Load settings and apply personalization
        /// </summary>
        protected virtual void LoadSettings()
        {
            this.Width = (Double)this.Settings["Win_Width"];
            this.Height = (Double)this.Settings["Win_Height"];
            this.WindowState = (WindowState)this.Settings["Win_State"];
            this._feedSettings = (Dictionary<String, FeedSetting>)this.Settings["FeedSettings"];
        }

        /// <summary>
        /// Save setting from object to settings properties
        /// </summary>
        protected virtual void SaveSettings()
        {
            this.Settings["Win_Width"] = this.Width;
            this.Settings["Win_Height"] = this.Height;
            if (this.WindowState != WindowState.Minimized)
                this.Settings["Win_State"] = this.WindowState;
            this.Settings["FeedSettings"] = this.FeedSettings;
        }

        /// <summary>
        /// Get Setting for specific feed
        /// </summary>
        /// <param name="feed"></param>
        /// <returns></returns>
        protected virtual FeedSetting GetFeedSetting(IFeed feed)
        {
            return this.GetFeedSetting(feed.Path);
        }

        /// <summary>
        /// Get Setting for specific feed path
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        protected virtual FeedSetting GetFeedSetting(string path)
        {
            FeedSetting fs;
            if (!this.FeedSettings.ContainsKey(path))
            {
                fs = new FeedSetting();
                this.FeedSettings[path] = fs;
            }
            else
                fs = this.FeedSettings[path];

            return fs;
        }

        private void PrepareTrayIcon()
        {
            notifyIcon = new System.Windows.Forms.NotifyIcon();

            Uri u = new Uri("pack://application:,,,/paperboy.ico", UriKind.RelativeOrAbsolute);
            // Create request for pack
            IWebRequestCreate requestCreate = new System.IO.Packaging.PackWebRequestFactory();
            WebRequest request = requestCreate.Create(u);
            // Get icon stream
            using (Stream stream = request.GetResponse().GetResponseStream())
            {
                notifyIcon.Icon = new System.Drawing.Icon(stream);
            }
            notifyIcon.Visible = false;

            // Prepare context menu items
            System.Windows.Forms.ContextMenu cm = new System.Windows.Forms.ContextMenu();
            System.Windows.Forms.MenuItem item = cm.MenuItems.Add("&Show feeds", ShowWindow);
            item.DefaultItem = true;
            cm.MenuItems.Add("-");
            cm.MenuItems.Add("&Exit", delegate { this.Close(); });

            notifyIcon.ContextMenu = cm;
            SetNotifyIconText();

            // Handle double click to restore window minimized
            notifyIcon.MouseDoubleClick += ShowWindow;

            this.feedsManager.RootFolder.PropertyChanged += MainWindow_PropertyChanged;
        }

        private void ShowWindow(object s, EventArgs e)
        {
            this.WindowState = WindowState.Normal;
        }

        private void PrepareData()
        {
            feedsManager = FeedsManager.Current;
            this.DataContext = feedsManager.RootFolder;

            // Prepare RootFolder items
            feedSource.Source = this.feedsManager.RootFolder.FoldersAndFeeds;
        }

        private void InitData()
        {
            // Prepare RootFolder items
            feedSource = new CollectionViewSource();
            ((ISupportInitialize)feedSource).BeginInit();
            feedSource.SortDescriptions.Add(new SortDescription("Type", ListSortDirection.Descending));
            feedSource.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
            feedSource.CollectionViewType = typeof(FeedListCollectionView);
            ((ISupportInitialize)feedSource).EndInit();
            this.Resources.Add("feedSource", feedSource);

            this.Resources.Add("feedToSettingsConverter", new FeedToSettingsConverter(this));
        }

        private void PrepareCommandBindings()
        {
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.Download, this.OnDownloadExecuted, this.OnDownloadCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.MarkAsRead, this.OnMarkAsReadExecuted, this.OnMarkAsReadCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.MarkAsUnread, this.OnMarkAsUnreadExecuted, this.OnMarkAsUnreadCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.MarkAllAsRead, this.OnMarkAllAsReadExecuted, this.OnMarkAllAsReadCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.Delete, this.OnDeleteExecuted, this.OnDeleteCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.DeleteItem, this.OnDeleteItemExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.SyncronizeAll, this.OnSyncronizeAllExecuted));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, this.OnCloseExecuted));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Properties, this.OnPropertiesExecuted, this.OnPropertiesCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.CopyItemLocation, this.OnCopyItemLocationExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.CopyLinkLocation, this.OnCopyLinkLocationExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.CopyCommentsLocation, this.OnCopyCommentsLocationExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.ViewComments, this.OnViewCommentsExecuted, this.OnViewCommentsCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.OpenItemInFeedTab, this.OnOpenItemInFeedExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.OpenItemInNewTab, this.OnOpenItemInNewTabExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.OpenItemInNewWindow, this.OnOpenItemInNewWindowExecuted, this.OnItemCommandCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.GotoNextUnreadItem, this.OnGotoNextUnreadItemExecuted, this.OnGotoNextUnreadItemCanExecute));
            this.CommandBindings.Add(new CommandBinding(FeedsCommands.About, this.OnAboutExecuted));
        }

        public IFeed CurrentFeed
        {
            get { return (IFeed)this.GetValue(CurrentFeedProperty); }
            set { this.SetValue(CurrentFeedProperty, value); }
        }

        public object CurrentFeedItem
        {
            get
            {
                IFeed feed = this.CurrentFeed;
                if (feed != null)
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(feed.Items);
                    return view.CurrentItem;
                }

                return null;
            }
            set
            {
                IFeed feed = this.CurrentFeed;
                if (feed != null)
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(feed.Items);
                    view.MoveCurrentTo(value);
                }
            }
        }

        public Dictionary<String, FeedSetting> FeedSettings
        {
            get { return this._feedSettings; }
        }

        protected ApplicationSettingsBase Settings
        {
            get { return this._applicationSettings; }
        }

        /// <summary>
        /// Method called when app need to navigate
        /// </summary>
        /// <param name="url"></param>
        /// <param name="targetName"></param>
        protected virtual void Navigate(Uri url, String targetName)
        {
        }

        private void SetNotifyIconText()
        {
            this.notifyIcon.Text = String.Format("Ricciolo - PaperBoy: {0} of {1} unreaded items", this.feedsManager.RootFolder.UnreadItemCount, this.feedsManager.RootFolder.TotalItemCount);
        }

        /// <summary>
        /// Create a setting property to store personalization
        /// </summary>
        /// <param name="name"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        protected virtual SettingsProperty CreateSettingsProperty(string name, object defaultValue)
        {
            SettingsProperty property = new SettingsProperty(name);
            property.PropertyType = defaultValue.GetType();
            property.Provider = this.Settings.Providers["LocalFileSettingsProvider"];
            if (defaultValue is String)
                property.SerializeAs = SettingsSerializeAs.String;
            else if (defaultValue.GetType().IsPrimitive || defaultValue.GetType().IsEnum)
                property.SerializeAs = SettingsSerializeAs.Xml;
            else
                property.SerializeAs = SettingsSerializeAs.Binary;
            property.DefaultValue = defaultValue;
            property.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());

            this.Settings.Properties.Add(property);

            return property;
        }

        private void MarkFolderAsRead(IFeedFolder folder)
        {
            // Iterate for each feed
            foreach (FeedBase f in folder.FoldersAndFeeds)
            {
                if (f is Feed)
                    ((Feed)f).MarkAllItemsRead();
                else if (f is FeedFolder)
                    MarkFolderAsRead((FeedFolder)f);
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            if (notifyIcon != null)
                notifyIcon.Dispose();
            notifyIcon = null;
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            try
            {
                this.CreateSettingsProperties();
                this.Settings.Reload();
                this.LoadSettings();
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, String.Format("You may loose the settings: {0}", ex.Message), "Error while load settings", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            PrepareData();
            PrepareTrayIcon();
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            SaveSettings();
        }

        protected override void OnStateChanged(EventArgs e)
        {
            base.OnStateChanged(e);

            if (this.WindowState == WindowState.Minimized)
            {
                this.notifyIcon.Visible = true;
                this.ShowInTaskbar = false;
            }
            else
            {
                this.notifyIcon.Visible = false;
                this.ShowInTaskbar = true;
            }
        }

        private static void OnCurrentFeedChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null || !(e.NewValue is Feed)) return;

            ((MainWindow)s).OnCurrendFeedChanged(s, e);
        }

        private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            SetNotifyIconText();
        }

        /// <summary>
        /// Called when selected feed was changed
        /// </summary>
        /// <param name="s"></param>
        /// <param name="e"></param>
        protected virtual void OnCurrendFeedChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null || !(e.NewValue is Feed)) return;

            // Get the view
            ICollectionView dataView = CollectionViewSource.GetDefaultView(((IFeed)e.NewValue).Items);

            // Set sorting expression
            if (dataView.SortDescriptions.Count == 0)
            {
                dataView.SortDescriptions.Add(new SortDescription("LocalId", ListSortDirection.Descending));
                dataView.CollectionChanged += dataView_CollectionChanged;
            }
        }

        private void dataView_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                ListCollectionView view = (ListCollectionView)sender;
                ((ICollectionView)view).CollectionChanged -= dataView_CollectionChanged;

                // Automatic go to the first unread item
                if (view.CurrentItem == null)
                    if (!MoveToNextUnreadItem(view)) // If no unread items, go to the first
                        view.MoveCurrentToFirst();
            }
        }

        private Boolean MoveToNextUnreadItem(CollectionView view)
        {
            for (int x = view.Count - 1; x >= 0; x--)
            {
                FeedItem item = (FeedItem)view.GetItemAt(x);
                if (item.IsNotRead)
                {
                    item.IsRead = true;

                    // Get view and set item
                    view.MoveCurrentTo(item);
                    return true;
                }
            }

            return false;
        }

        #region Command bindings

        protected virtual void OnDownloadExecuted(object s, ExecutedRoutedEventArgs e)
        {
            Feed f = this.CurrentFeed as Feed;
            if (f != null)
                f.AsyncDownload();
        }

        protected virtual void OnCloseExecuted(object s, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }

        protected virtual void OnAboutExecuted(object s, ExecutedRoutedEventArgs e)
        {
            AboutForm af = new AboutForm();
            af.Owner = this;
            af.ShowDialog();
        }

        protected virtual void OnSyncronizeAllExecuted(object s, ExecutedRoutedEventArgs e)
        {
            this.feedsManager.SyncronizeAll();
        }

        protected virtual void OnDownloadCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeed != null && this.CurrentFeed is Feed);
        }

        protected virtual void OnDeleteExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeed is IDelete)
                ((IDelete)this.CurrentFeed).Delete();
        }

        protected virtual void OnDeleteCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeed is IDelete);
        }

        protected virtual void OnGotoNextUnreadItemExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeed != null)
            {
                IFeed feed = this.CurrentFeed;

                bool found;
                ListCollectionView parentView = null;
                do
                {
                    found = feed.HasUnreadItems;

                    // Try to look into sibling
                    if (!found)
                    {
                        int index;
                        int? startIndex = null;
                        IFeedFolder parent = feed.Parent;
                        do
                        {
                            parentView = (ListCollectionView)CollectionViewSource.GetDefaultView(parent.FoldersAndFeeds);
                            if (feed != null)
                                index = parentView.IndexOf(feed);
                            else
                                index = -1;
                            if (!startIndex.HasValue)
                                startIndex = index;
                            index++;

                            // Go to next feed
                            if (parentView.Count > index)
                            {
                                feed = (IFeed)parentView.GetItemAt(index);
                                if (feed is FeedFolder && ((FeedFolder)feed).HasUnreadItems)
                                {
                                    parent = (FeedFolder)feed;
                                    feed = null;
                                }
                            }
                            else if (startIndex > 0 && index > startIndex)
                            {
                                // Try starting from begin
                                feed = null;
                            }
                            else
                            {
                                // Go to the parent
                                parent = parent.Parent;
                                feed = null;

                                // Found root
                                if (parent == null)
                                    return;
                            }
                        }
                        while (!(feed is Feed));
                    }
                    else if (parentView != null) // Auto select new feed
                    {
                        parentView.MoveCurrentTo(feed);
                        this.CurrentFeed = feed;
                    }
                    else // Feed was not changed
                        MoveToNextUnreadItem((ListCollectionView)CollectionViewSource.GetDefaultView(feed.Items));
                }
                while (!found);
            }
        }

        protected virtual void OnGotoNextUnreadItemCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.feedsManager.RootFolder.HasUnreadItems;
        }

        protected virtual void OnPropertiesExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeed is Feed)
            {
                // Prepare data
                FeedSettings fs = new FeedSettings(this.feedsManager, (Feed)this.CurrentFeed);

                FeedSettingsForm fsf = new FeedSettingsForm(fs);
                fsf.Owner = this;
                if (fsf.ShowDialog().Value)
                    fs.Commit();
            }
        }

        protected virtual void OnPropertiesCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeed is Feed);
        }

        protected virtual void OnDeleteItemExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is IDelete)
                ((IDelete)this.CurrentFeedItem).Delete();
        }

        protected virtual void OnOpenItemInFeedExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
                this.Navigate(((FeedItem)this.CurrentFeedItem).Link, "Feed");
        }

        protected virtual void OnOpenItemInNewTabExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
                this.Navigate(((FeedItem)this.CurrentFeedItem).Link, IETabControl.BlankTarget);
        }

        protected virtual void OnOpenItemInNewWindowExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
            {
                Process.Start(((FeedItem)this.CurrentFeedItem).Link.ToString());
            }
        }

        protected virtual void OnViewCommentsCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeedItem is FeedItem && ((FeedItem)this.CurrentFeedItem).Comments != null);
        }

        protected virtual void OnViewCommentsExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
                this.Navigate(((FeedItem)this.CurrentFeedItem).Comments, IETabControl.BlankTarget);
        }

        protected virtual void OnCopyItemLocationExecuted(object s, ExecutedRoutedEventArgs e)
        {
            FeedItem item = this.CurrentFeedItem as FeedItem;
            if (item != null)
                Clipboard.SetText(item.DownloadUrl.ToString(), TextDataFormat.UnicodeText);
        }

        protected virtual void OnCopyLinkLocationExecuted(object s, ExecutedRoutedEventArgs e)
        {
            FeedItem item = this.CurrentFeedItem as FeedItem;
            if (item != null)
                Clipboard.SetText(item.Link.ToString(), TextDataFormat.UnicodeText);
        }

        protected virtual void OnCopyCommentsLocationExecuted(object s, ExecutedRoutedEventArgs e)
        {
            FeedItem item = this.CurrentFeedItem as FeedItem;
            if (item != null && item.Comments != null)
                Clipboard.SetText(item.Comments.ToString(), TextDataFormat.UnicodeText);
        }

        protected virtual void OnItemCommandCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeedItem != null);
        }

        protected virtual void OnMarkAsReadExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
                ((FeedItem)CurrentFeedItem).IsRead = true;
        }

        protected virtual void OnMarkAsUnreadExecuted(object s, ExecutedRoutedEventArgs e)
        {
            if (this.CurrentFeedItem is FeedItem)
            {
                ((FeedItem)this.CurrentFeedItem).IsRead = false;
                ((FeedItem)this.CurrentFeedItem).SkipMarkAsRead = true;
            }
        }

        protected virtual void OnMarkAllAsReadExecuted(object s, ExecutedRoutedEventArgs e)
        {
            IFeed feed = this.CurrentFeed;
            if (feed == null) return;

            // Mark readed single feed
            if (feed is Feed)
                ((Feed)feed).MarkAllItemsRead();
            else if (feed is FeedFolder)
            {
                MarkFolderAsRead((FeedFolder)feed);
            }
        }

        protected virtual void OnMarkAsReadCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeedItem is FeedItem && ((FeedItem)this.CurrentFeedItem).IsNotRead);
        }

        protected virtual void OnMarkAllAsReadCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeed != null && this.CurrentFeed.HasUnreadItems);
        }

        protected virtual void OnMarkAsUnreadCanExecute(object s, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (this.CurrentFeedItem is FeedItem && ((FeedItem)this.CurrentFeedItem).IsRead);
        }

        #endregion
    }
}